package com.alpaca.recommend.mapreduce;

import com.alpaca.recommend.bean.URecommend;
import com.alpaca.recommend.dto.UserItemDTO;
import com.alpaca.recommend.hbase.HTableClients;
import com.alpaca.recommend.util.MD5Util;
import com.alpaca.recommend.util.SpringContextLoader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Created by wangj on 2015/2/10.
 */
public class CalculateURecommend {
    private static final Logger logger = LoggerFactory.getLogger(CalculateURecommend.class);

    public static final byte[] ITEM_BYTES = "i".getBytes();
    public static final byte[] RANK_BYTES = "r".getBytes();
    public static final byte[] ITEM_IS_READ_BYTES = "ir".getBytes();
    public static final byte[] ITEM_IDS_BYTES = Bytes.toBytes("ids");

    static class  CalUserItemRankMapper extends TableMapper<Text,Text> {
        protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
            Cell rankCell = value.getColumnLatestCell(ITEM_BYTES, RANK_BYTES);
            if (rankCell == null) {
                logger.info("rank is null ,with key:"+Bytes.toString(key.get()));
                return;
            }

            int rank = Bytes.toInt( CellUtil.cloneValue(rankCell));
            Cell isReadCell = value.getColumnLatestCell(ITEM_BYTES , ITEM_IS_READ_BYTES);
            int isRead = (isReadCell == null ? 0 : Bytes.toInt(CellUtil.cloneValue(isReadCell)));
            String rowKey = Bytes.toString(key.get()).substring(0,32);
            String itemId = Bytes.toString(key.get()).substring(32);

            context.write(new Text(rowKey) ,new Text(itemId +"_"+ rank * (isRead == 1 ? 1 :2)));
        }
    }

    static class  CalUserItemRankReduce extends TableReducer<Text,Text , ImmutableBytesWritable> {
        @Override
        protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {

            List<UserItemDTO> rankList = new ArrayList<UserItemDTO>();
            for (Text itemRank : values){
                String itemRankStr = new String(itemRank.getBytes());

                rankList.add(new UserItemDTO(itemRankStr));
            }
            Collections.sort(rankList , new Comparator<UserItemDTO>() {
                @Override
                public int compare(UserItemDTO o1, UserItemDTO o2) {
                    return o2.getRank() - o1.getRank();
                }
            });

            int count = 0;
            StringBuilder sb = new StringBuilder();
            for (UserItemDTO rank : rankList) {
                if (count < 1000) {
                    sb.append(rank.getItemId()).append(",");
                }
                count ++;
            }

            if (count < 1000) {
                String defaultRowKey = MD5Util.md5(0) + key.toString().substring(16 , 32);
                URecommend uRecommend = URecommend.dao.get(defaultRowKey);

                if (uRecommend != null) {
                    String[] defaultRecommendIds = uRecommend.getString("r", "ids").split(",");
                    for (String recommendId : defaultRecommendIds) {
                        if (count < 1000) {
                            sb.append(recommendId).append(",");
                        }
                        count++;
                    }
                }
            }


            String ids = sb.substring(0 , sb.length() - 1);

            Put put = new Put(Bytes.toBytes(key.toString()));
            put.add(RANK_BYTES, ITEM_IDS_BYTES, Bytes.toBytes(ids));

            context.write(null, put);
        }
    }

    public static boolean executeJob() throws IOException, ClassNotFoundException, InterruptedException {
        System.out.println("1111111");
        Configuration conf = SpringContextLoader.getBean(HTableClients.class).getConf();
        String table = "u_items";
        String reduceTable = "u_recommends";

        Job job = Job.getInstance(conf,"Calculate to u_items rank");
        job.setJarByClass(CalculateURecommend.class);

        Scan scan = new Scan();
        scan.setCaching(500);        // 1 is the default in Scan, which will be bad for MapReduce jobs
        scan.setCacheBlocks(false);  // don't set to true for MR jobs

        TableMapReduceUtil.initTableMapperJob(
                table,
                scan,
                CalUserItemRankMapper.class,
                Text.class,
                Text.class,
                job);

        TableMapReduceUtil.initTableReducerJob(
                reduceTable,        // output table
                CalUserItemRankReduce.class,    // reducer class
                job);
        job.setNumReduceTasks(1);
        boolean b = job.waitForCompletion(true);
        return b;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        CalculateURecommend recommend = new CalculateURecommend();
        recommend.executeJob();
    }


}
